#pragma once
#define YAML_CPP_STATIC_DEFINE
#include "yaml-cpp/yaml.h"

#include "fxcc/core/pch.h"


namespace fs = std::filesystem;

namespace fxcc
{

    namespace yml
    {
        struct FXCC_API Serializer
        {

            template <typename T>
            void Encode(YAML::Node &node, const T &t);

            template <typename T>
            bool Decode(const YAML::Node &node, T &t);

            template <typename T>
            void SetVariable(YAML::Node &node, const std::string &key, const T &t)
            {
                YAML::Node newNode;

                Encode<T>(newNode, t);
                node[key] = newNode;
            }

       

            template <typename T>
            bool DecodePath(const std::string &source, T &t)
            {
                if (!fs::exists(source))
                {
                    return false;
                }
                if (fs::is_directory(source))
                {
                    return false;
                }
                YAML::Node node = YAML::LoadFile(source);
                if (!node.IsDefined())
                {
                    return false;
                }
                Decode<T>(node, t);

                return true;
            };
            template <typename EnumType>
            void SetVariableEnum(YAML::Node& node, const std::string &label, const EnumType &t)
            {
                int v = (int)t;
                node[label] = v;
            };
            template <typename EnumType>
            bool GetVariableEnum(const YAML::Node& node, const std::string &label, EnumType &t)
            {
                bool res = node[label].IsDefined();
                if (res)
                {
                    int v = node[label].as<int>();
                    memcpy(&t, &v, sizeof(EnumType));
                }
                return res;
            };
            template <typename T>
            bool EncodePath(const std::string &targetPath, const T &t)
            {
                //if (fs::exists(targetPath))
                //{
                //    fs::remove(targetPath);
                //}
        /*        fs::path _p(targetPath);
                if (_p.has_parent_path())
                {
                    if (!fs::exists(_p.parent_path()))
                    {
                        fs::create_directories(_p.parent_path());
                    }
                }*/
                fxcc::Base::CreateParentDirs(targetPath);
                std::ofstream ofs(targetPath);
                if (!ofs.is_open())
                {
                    ztclog::info("failed open %s", targetPath.c_str());
                    ofs.close();
                    return false;
                }
                YAML::Emitter emitter;
                YAML::Node node;
                Encode<T>(node, t);
                emitter << node;

                ofs << emitter.c_str();

                ofs.flush();
                ofs.close();
                return true;
            }

            template <typename T>
            void SetArrayVariable(YAML::Node &node, const std::string &key, const std::vector<T> &ts,
                std::function<bool(const T&)> func = 0)
            {
                YAML::Node fieldNode;

                for (const auto &t : ts)
                {
                    bool filter = true;
                    if (func)
                    {
                        filter = func(t);
                    }
                    if (filter)
                    {
                        YAML::Node itNode;
                        Encode<T>(itNode, t);
                        fieldNode.push_back(itNode);
                    }
                }
                node[key] = fieldNode;
            }

            template <typename T>
            void SetArrayVariable2(YAML::Node& node, const std::string& key, const T* ts, int num,
                std::function<bool(const T&)> func = 0)
            {
                YAML::Node fieldNode;

                for ( int i = 0; i < num; i++)
                {
                    const auto& t = ts[i];
                    bool filter = true;
                    if (func)
                    {
                        filter = func(t);
                    }
                    if (filter)
                    {
                        YAML::Node itNode;
                        Encode<T>(itNode, t);
                        fieldNode.push_back(itNode);
                    }

          
                }
                node[key] = fieldNode;
            }

            template <typename T>
            void SetArrayVariable2(YAML::Node& node, const std::string& key, const std::vector<T>& ts,
                std::function<bool(const T&)> func = 0)
            {
                YAML::Node fieldNode;

                for (const auto& t : ts)
                {
                    bool filter = true;
                    if (func)
                    {
                        filter = func(t);
                    }
                    if (filter)
                    {
                        YAML::Node itNode;
                        Encode<T>(itNode, t);
                        fieldNode.push_back(itNode);
                    }
                }
                node[key] = fieldNode;
            }

        
            template <typename T>
            void SetPtrArrayVariable(YAML::Node &node, const std::string &key, const std::vector<std::shared_ptr<T>> &ts,
                std::function<bool(const T&)> func = 0)
            {
                YAML::Node fieldNode;

                for (const auto t : ts)
                {
                    bool flag = true;
                    if (func)
                    {
                        flag = func(*t);
                    }
                    if (flag)
                    {
                        YAML::Node itNode;
                        Encode<T>(itNode, *t);
                        fieldNode.push_back(itNode);
                    }

                }
                node[key] = fieldNode;
            }

            template <typename T>
            void SetPtrArrayVariable0(YAML::Node& node, const std::string& key, const std::vector<T*>& ts,
                std::function<bool(const T&)> func = 0
            )
            {
                YAML::Node fieldNode;

                for (const auto t : ts)
                {
                    YAML::Node itNode;
                    Encode<T>(itNode, *t);
                    fieldNode.push_back(itNode);
                }
                node[key] = fieldNode;
            }

            template <typename T>
            void SetMapVariable(YAML::Node& node, const std::string& key, const std::map<std::string, T>& ts,
                std::function<bool(const T& t)> func=0)
            {
                if (ts.empty())
                {
                    return;
                }

                YAML::Node fieldNode;

                for (const auto& [mapKey, mapValue] : ts) 
                {
                    bool filter = true;
                    if (func)
                    {
                        filter = func(mapValue);
                    }
                    if(filter)
                        Encode<T>(fieldNode[mapKey], mapValue);
                }

                node[key] = fieldNode;
            }
            
            template <typename T>
            void SetMapVariableInt(YAML::Node& node, const std::string& key, const std::map<int, T>& ts,
                std::function<bool(const T& t)> func = 0)
            {
                YAML::Node fieldNode;

                for (const auto& [mapKey, mapValue] : ts)
                {
                    bool filter = true;
                    if (func)
                    {
                        filter = func(mapValue);
                    }
                    if (filter)
                        Encode<T>(fieldNode[std::to_string(mapKey)], mapValue);
                }

                node[key] = fieldNode;
            }

            template <typename T>
            void SetMapVariableKeyFilter(YAML::Node& node, const std::string& key, const std::map<std::string, T>& ts,
                std::function<bool(const T& t)> func = 0, std::function<bool(const std::string& t)> keyfunc = 0)
            {
                YAML::Node fieldNode;

                for (const auto& [mapKey, mapValue] : ts)
                {
                    bool filter = true;
                    if (func)
                    {
                        filter = func(mapValue);
                    }
                    bool filter2 = true;
                    if (keyfunc)
                    {
                        filter2 = keyfunc(mapKey);
                    }
                    if (filter && filter2)
                        Encode<T>(fieldNode[mapKey], mapValue);
                }

                node[key] = fieldNode;
            }


            template <typename T>
            bool GetMapVariableInt(const YAML::Node& node, const std::string& key, std::map<int, T>& ts)
            {
                if (!node[key])
                    return false;

                ts.clear();
                YAML::Node fieldNode = node[key];

                for (const auto& it : fieldNode)
                {
                    T value;
                    if (!Decode<T>(it.second, value))
                        return false;
                    ts[it.first.as<int>()] = value;
                }
                return true;
            }

            template <typename T>
            bool GetMapVariable(const YAML::Node& node, const std::string& key, std::map<std::string, T>& ts)
            {
                if (!node[key].IsDefined()) 
                    return false;

                ts.clear();
                YAML::Node fieldNode = node[key];

                for (const auto& it : fieldNode)
                {
                    T value;
                    if (!Decode<T>(it.second, value))
                        return false;
                    ts[it.first.as<std::string>()] = value;
                }
                return true;
            }


           
            template <typename T>
            bool GetArrayVariable(const YAML::Node &node, const std::string &key, std::vector<T> &ts)
            {
                if (!node || !node.IsMap() || !node[key] || !node[key].IsSequence())
                {
                    return false;
                }

                ts.clear();

                YAML::Node fieldNode = node[key];
                if (!fieldNode.IsDefined())
                {
                    return false;
                }

                for (const auto &itNode : fieldNode)
                {
                    T t;
                    if (Decode<T>(itNode, t))
                    {
                        ts.push_back(t);
                    }
                }
                return true;
            }

            template <typename T>
            bool GetArrayVariable2(const YAML::Node& node, const std::string& key, T* ts, int num)
            {
                YAML::Node fieldNode = node[key];
                if (!fieldNode.IsDefined())
                {
                    return false;
                }
                int index = 0;

                for (const auto& itNode : fieldNode)
                {
                    if (!itNode.IsDefined())
                    {
                        break;
                    }
                    if (Decode<T>(itNode, ts[index]))
                    {

                        
                    }
                    index++;
                    auto t = ts[index];

                    if (index == num - 1)
                    {
                        break;
                    }

                }
                return true;
            }


            template <typename T>
            bool GetPtrArrayVariable(const YAML::Node &node, const std::string &key, std::vector<std::shared_ptr<T>> &ts)
            {
                YAML::Node fieldNode = node[key];
                if (!fieldNode.IsDefined())
                {
                    return false;
                }

                for (const auto &itNode : fieldNode)
                {
                    std::shared_ptr<T> t = std::make_shared<T>();
                    if (Decode<T>(itNode, *t))
                    {
                        ts.push_back(t);
                    }
                }
                return true;
            }

            template <typename T>
            bool GetVariable(const YAML::Node &node, const std::string &key, T &t)
            {
                YAML::Node childNode = node[key];
                if (!childNode.IsDefined())
                {
                    return false;
                }
                return Decode<T>(childNode, t);
            }

           
    
        };
    };
};